#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "hlog.h"
#include "loser_tree.h"
#include "hlog_internal.h"

static void get_key(int idx, void **key);
static void append_to_log_file(struct timespec *ts, char *buf1, int len1,
		char *buf2, int len2);

/* mit_ts 和 max_ts是败者树算法需要用到的两个特殊时间戳 */
static struct timespec min_ts = {0, 0};
static struct timespec max_ts = {LONG_MAX, 0};

int g_last_words_piro;

static struct hlog_ctrl *g_ctrl;
static __thread struct hlog_thr *g_thr;

static char *g_prio_str[] = {
	"NULL  ",
	"INFO  ",
	"NOTICE",
	"WARN  ",
	"ERROR ",
};

static int is_max_ts(struct timespec *ts)
{
	return ts->tv_sec == max_ts.tv_sec;
}

/*
 * 比较两个timespec结构。如果两个时间相等，则返回0；如果第一个时间更早，则返回
 * 返回负数；否则返回正数；
 */
static long cmp_ts(struct timespec *ts1, struct timespec *ts2)
{
	long diff_sec, diff_nsec;

	diff_sec = ts1->tv_sec - ts2->tv_sec;
	if (diff_sec != 0)
		return diff_sec;

	diff_nsec = ts1->tv_nsec - ts2->tv_nsec;
	return diff_nsec;
}

static void ts_add_ns(struct timespec *ts, long ns)
{
	ts->tv_nsec += ns;
	if (ts->tv_nsec > 1000000000L) {
		ts->tv_sec += 1;
		ts->tv_nsec -= 1000000000L;
	}
}


/* 返回环形缓冲区中剩余空间的字节数 */
static int _ring_room(struct hlog_ring *r)
{
	int occupied, room;

	occupied = r->head - r->tail;
	if (occupied < 0)
		occupied += HLOG_THR_BUF_LEN;
	room = HLOG_THR_BUF_LEN - occupied;

	return room;
}

static int _ring_is_empty(struct hlog_ring *r)
{
	return r->head == r->tail;
}

void dump_thr(char *msg, void *p)
{
	int i, th, n;
	struct hlog_thr *thr;

	fprintf(stderr, "\n%s, nr_thr=%d, p=%p\n", msg, g_ctrl->nr_thr, p);
	n = 0;
	for (i = 0; i < HLOG_MAX_THR; i++) {
		if (g_ctrl->thr[i] == NULL)
			continue;
		thr = g_ctrl->thr[i];
		n++;
		if (i != thr->th_idx)
			fprintf(stderr, " *** ");
		fprintf(stderr,
			"n=%d, thr[%d]={th_idx=%d, lt_idx=%d, tid=%d}, ring{head=%d, tail=%d}\n",
			n, i, thr->th_idx, thr->lt_idx, thr->tid,
			thr->ring->head, thr->ring->tail);
	}
	for (i=0; i < g_ctrl->nr_thr; i++) {
		th = g_ctrl->lt_map[i];
		thr = g_ctrl->thr[th];
		if (th != thr->th_idx)
			fprintf(stderr, " *** ");
		fprintf(stderr,
			"lt_map[%d]=%d, {th_idx=%d, lt_idx=%d, tid=%d}, ring{head=%d, tail=%d}\n",
			i, g_ctrl->lt_map[i], thr->th_idx, thr->lt_idx,
			thr->tid, thr->ring->head, thr->ring->tail);
	}
}

void hlog_dump(void)
{
	int th, i;
	long nr_blocked;
	struct hlog_thr *thr;

	pthread_mutex_lock(&g_ctrl->mutex);

	nr_blocked = 0;
	for (i = 0; i < g_ctrl->nr_thr; i++) {
		th = g_ctrl->lt_map[i];
		thr = g_ctrl->thr[th];
		nr_blocked += thr->nr_blocked;
		thr->nr_blocked = 0;
	}
	printf(
		"nr_logs=%-7ld nr_wait=%-5ld nr_refresh=%-10ld nr_wakeup=%-6ld, nr_blocked=%-6ld\n",
		g_ctrl->nr_logs, g_ctrl->nr_wait, g_ctrl->nr_refresh,
		g_ctrl->nr_wakeup, nr_blocked);

	g_ctrl->nr_logs = 0;
	g_ctrl->nr_wait = 0;
	g_ctrl->nr_refresh = 0;
	g_ctrl->nr_wakeup = 0;
	pthread_mutex_unlock(&g_ctrl->mutex);
}

/* 业务线程退出的时候，调用本函数 */
static void thr_exit(void *arg)
{
	struct hlog_thr *thr;
	int ret, lt_idx, last;


	thr = (struct hlog_thr *)arg;

	pthread_mutex_lock(&thr->mutex);
	// 等待ring清空
	while (!_ring_is_empty(thr->ring)) {
		struct timespec ts;

		if (LOAD(g_ctrl->status) == LS_WAIT) {
			STORE(g_ctrl->status, LS_POLL);
			pthread_cond_signal(&g_ctrl->logger_cond);
		}
		thr->blocked++;
		clock_gettime(CLOCK_REALTIME, &ts);
		ts_add_ns(&ts, 100000L);		//100us
		pthread_cond_timedwait(&thr->thr_cond, &thr->mutex, &ts);
	}
	pthread_mutex_unlock(&thr->mutex);

	ret = pthread_mutex_lock(&g_ctrl->mutex);
	assert(ret == 0);
	pthread_cond_destroy(&thr->thr_cond);
	pthread_mutex_destroy(&thr->mutex);
	ret = pthread_key_delete(thr->key);
	assert(ret == 0);

	g_ctrl->thr[thr->th_idx] = NULL;
	lt_idx = thr->lt_idx;
	free(thr);

	g_ctrl->nr_thr--;
	last = g_ctrl->lt_map[g_ctrl->nr_thr];
	if (g_ctrl->nr_thr>0 && lt_idx!=g_ctrl->nr_thr) {
		thr = g_ctrl->thr[last];
		assert(thr->th_idx == last);
		assert(g_ctrl->nr_thr == thr->lt_idx);
		g_ctrl->lt_map[lt_idx] = thr->th_idx;
		thr->lt_idx = lt_idx;
	}
	lt_refresh(g_ctrl->lt, g_ctrl->nr_thr);
	ret = pthread_mutex_unlock(&g_ctrl->mutex);
	assert(ret == 0);

}


/* 初始化每线程结构 */
static void init_thr(void)
{
	int i, ret;

	assert(g_ctrl != NULL);

	ret = pthread_mutex_lock(&g_ctrl->mutex);
	assert(ret == 0);

	if (g_ctrl->nr_thr >= HLOG_MAX_THR) {
		g_ctrl->thr_err = ENOENT;
		pthread_cond_signal(&g_ctrl->logger_cond);
		goto done;
	}

	/* 查找空的thread槽位 */
	for (i = 0; i < HLOG_MAX_THR; i++)
		if (g_ctrl->thr[i] == NULL)
			break;

	assert(i < HLOG_MAX_THR);

	g_thr = (struct hlog_thr *)calloc(1, sizeof(struct hlog_thr));
	if (g_thr == NULL) {
		g_ctrl->thr_err = ENOMEM;
		pthread_cond_signal(&g_ctrl->logger_cond);
		goto done;
	}
	g_thr->tid = gettid();
	g_thr->th_idx = i;
	g_thr->ring = (struct hlog_ring *)(g_ctrl->shared_mem +
					i * HLOG_RING_SIZE);
	g_thr->lt_idx = g_ctrl->nr_thr;

	pthread_cond_init(&g_thr->thr_cond, NULL);
	pthread_mutex_init(&g_thr->mutex, NULL);

	g_ctrl->lt_map[g_ctrl->nr_thr] = i;
	g_ctrl->thr[i] = g_thr;
	g_ctrl->nr_thr++;

	/* 这个pthread_key的目的是为了截获pthread_exit事件 */
	ret = pthread_key_create(&g_thr->key, thr_exit);
	assert(ret == 0);

	ret = pthread_setspecific(g_thr->key, g_thr);
	assert(ret == 0);

	lt_refresh(g_ctrl->lt, g_ctrl->nr_thr);

done:
	ret = pthread_mutex_unlock(&g_ctrl->mutex);
	assert(ret == 0);
}

/* 把一条日志放入环形缓冲区空间 */
static void _ring_enq(struct hlog_ring *r, struct timespec *log_ts, int prio,
	char *buf, int buf_len, int cap)
{
	char *p;
	int head, next_head;
	struct hlog_entry_hdr *entry;

	head = r->head;

	next_head = (head + cap) % HLOG_THR_BUF_LEN;

	/* head所指向的位置，至少剩下一个entry的空间 */
	p = &r->buf[head];
	entry = (struct hlog_entry_hdr *)p;
	entry->type = ENTRY_STRING;
	entry->prio = (char)prio;
	entry->cap = cap;
	entry->len = buf_len;
	entry->ts = *log_ts;

	//fprintf(stderr, "ring_enq: buf_len=%d\n", buf_len);

	if (next_head > head) {
		/* 没有翻圈折返 */
		p += sizeof(*entry);
		memcpy(p, buf, buf_len);
	} else {
		/* 翻圈折返的情况 */
		int first, second;

		first = HLOG_THR_BUF_LEN - head - sizeof(*entry);
		assert(first >= 0);

		second = buf_len - first;

		if (first > 0) {
			p += sizeof(*entry);
			memcpy(p, buf, first);
		}

		if (second > 0) {
			p = &r->buf[0];
			memcpy(p, buf + first, second);
		}
	}

	STORE(r->head, next_head);
}

static void _ring_peak_ts(struct hlog_ring *r, struct timespec **log_ts)
{
	int tail;
	struct hlog_entry_hdr *entry;

	assert(!_ring_is_empty(r));

	tail = r->tail;
	entry = (struct hlog_entry_hdr *)(r->buf + tail);
	*log_ts = &entry->ts;
}

static void _ring_peak(struct hlog_ring *r, struct timespec *log_ts, int *prio,
	char **buf1, int *buf1_len, char **buf2, int *buf2_len, int *_next_tail)
{
	int tail, next_tail;
	struct hlog_entry_hdr *entry;

	assert(!_ring_is_empty(r));

	tail = r->tail;

	entry = (struct hlog_entry_hdr *)(r->buf + tail);
	next_tail = (tail + entry->cap) % HLOG_THR_BUF_LEN;
	*log_ts = entry->ts;
	*prio = entry->prio;

	if (next_tail > tail) {
		/* 没有翻圈折返 */
		*buf1 = (char *)(entry + 1);
		*buf1_len = entry->len;
		*buf2 = NULL;
		*buf2_len = 0;
	} else {
		/* 翻圈折返的情况 */
		int left;

		left = (HLOG_THR_BUF_LEN - tail - sizeof(*entry));
		assert(left >= 0);

		if (left > 0) {
			*buf1 = (char *)(entry + 1);
			*buf1_len = (left < entry->len ? left : entry->len);

		} else {
			*buf1 = NULL;
			*buf1_len = 0;
		}

		if (entry->len - left > 0) {
			*buf2 = (char *)r->buf;
			*buf2_len = entry->len - left;
		} else {
			*buf2 = NULL;
			*buf2_len = 0;
		}
	}

	*_next_tail = next_tail;
}

/* 把环形缓冲区中尾部的一个日志记录删除 */
static void _ring_deq(struct hlog_ring *r, int next_tail)
{
	r->tail = next_tail;
}

static struct hlog_thr *get_thr(int idx)
{
	int th_idx;
	struct hlog_thr *thr;

	th_idx = g_ctrl->lt_map[idx];
	thr = g_ctrl->thr[th_idx];

	return thr;
}

static void get_key(int idx, void **key)
{
	struct hlog_thr *thr;
	struct hlog_ring *r;
	struct timespec *ts;

	if (idx >= g_ctrl->nr_thr) {
		*key = (void *)&min_ts;
		return;
	}

	thr = get_thr(idx);

	r = thr->ring;
	if (_ring_is_empty(r)) {
		*key = (void *)&max_ts;
		return;
	} else {
		_ring_peak_ts(r, &ts);
		if (cmp_ts(ts, &g_ctrl->last_refresh) < 0)
			*key = (void *)ts;
		else
			*key = (void *)&max_ts;
	}
}

static void hlog_write(struct timespec *log_ts, int prio, char *buf, int buf_len)
{
	struct hlog_ring *r;
	int cap, len;
	int ret;
	struct timespec ts;

	len = sizeof(struct hlog_entry_hdr) + buf_len;

	/* round up to HLOG_ENTRY_ALIGN */
	cap = (len + HLOG_ENTRY_ALIGN - 1) / HLOG_ENTRY_ALIGN;
	cap *= HLOG_ENTRY_ALIGN;
	r = g_thr->ring;

	pthread_mutex_lock(&g_thr->mutex);
	/* 如果环形缓冲区中空间不足以放下这条日志，那么等待 */
	while (_ring_room(r) < cap) {

		g_thr->blocked++;
		g_thr->nr_blocked++;

		clock_gettime(CLOCK_REALTIME, &ts);
		ts_add_ns(&ts, 1000000L);		//1ms

		pthread_cond_timedwait(&g_thr->thr_cond, &g_thr->mutex, &ts);
	}
	_ring_enq(r, log_ts, prio, buf, buf_len, cap);
	pthread_mutex_unlock(&g_thr->mutex);

	/* 初始化之后，g_ctrl->persist_prio的值不再改变，相当于只读变量 */
	if (prio >= g_ctrl->persist_prio) {
		ret = msync(r, g_ctrl->page_size, MS_SYNC);
		assert(ret == 0);
	}

	/*
	 * 如果logger处于WAIT状态，则唤醒它。下面这个判断，可能存在通知信号丢失
	 * 的风险。持有mutex的情况下来比较status则不会出现信号丢失，但是会影响
	 * 性能。在logger进程中采用pthread_cond_timedwait来解决信号丢失问题。
	 */
	if (LOAD(g_ctrl->status) == LS_WAIT) {
		pthread_mutex_lock(&g_ctrl->mutex);
		STORE(g_ctrl->status, LS_POLL);
		pthread_mutex_unlock(&g_ctrl->mutex);
		pthread_cond_signal(&g_ctrl->logger_cond);
	}
}


void hlog_log(struct hlog_point *hp, ...)
{
	char buf[HLOG_MAX_LEN];
	va_list ap;
	int len;
	struct timespec ts;

	if (unlikely(g_thr == NULL))
		init_thr();

	clock_gettime(CLOCK_REALTIME, &ts);

	len = snprintf(buf, HLOG_MAX_LEN, "%s %d %s(%d) -- ",
			g_prio_str[hp->prio], g_thr->tid, hp->src, hp->line_no);

	va_start(ap, hp);
	len += vsnprintf(buf + len, HLOG_MAX_LEN - len, hp->fmt, ap);
	va_end(ap);

	hlog_write(&ts, hp->prio, buf, len);
}

/* 从共享内存区中找出上一次进程崩溃之前没来得及写入日志文件信息，写入日志文件 */
static void recover_last_words(void)
{
	char *buf1, *buf2;
	int len, ret;
	int len1, len2;
	int next_tail;
	struct hlog_ring *r;
	int i, prio;
	struct timespec log_ts;
	char buf[64];

	len = snprintf(buf, sizeof(buf),
		"\n============ recovered log message =================\n");
	ret = write(g_ctrl->log_fd, buf, len);
	assert(ret == len);

	for (i = 0; i < HLOG_MAX_THR; i++) {
		r = (struct hlog_ring *)(g_ctrl->shared_mem + i*HLOG_RING_SIZE);
		while (!_ring_is_empty(r)) {
			_ring_peak(r, &log_ts, &prio, &buf1, &len1, &buf2,
				&len2, &next_tail);
			append_to_log_file(&log_ts, buf1, len1, buf2, len2);
			_ring_deq(r, next_tail);
		}
	}
	snprintf(buf, sizeof(buf),
		"\n========== end of recovered log message ===============\n");
	ret = write(g_ctrl->log_fd, buf, len);
	assert(ret == len);
}

/*
 * 初始化共享内存区
 *
 * 共享内存区用来存放业务线程缓存的日志信息, logger线程从共享内存区中取出日志
 * 信息，写入日志文件。当进程崩溃的时候，共享内存区里面日志条目不会消失。进程
 * 再次启动的时候，可以从共享内存区中把暂存的日志信息恢复出来，重新写入日志
 * 文件。
 */
static int init_shared_mem(void)
{
	int ret, flags;
	size_t len;
	struct stat st;

	flags = O_RDWR;
	ret = stat(g_ctrl->mmap_file, &st);
	if (ret < 0) {
		assert(errno == ENOENT);
		flags |= O_CREAT;
	}

	ret = open(g_ctrl->mmap_file, flags, 0644);
	if (ret < 0)
		perror("open");
	assert(ret >= 0);
	g_ctrl->mmap_fd = ret;

	len = HLOG_RING_SIZE;
	assert((len & (len-1)) == 0);		//长度必须对齐到2**n

	len *= HLOG_MAX_THR;
	ret = ftruncate(g_ctrl->mmap_fd, len);
	assert(ret == 0);

	flags = MAP_SHARED|MAP_LOCKED|MAP_POPULATE;
	g_ctrl->shared_mem = mmap(NULL, len, PROT_READ|PROT_WRITE,
			flags, g_ctrl->mmap_fd, 0);
	assert(g_ctrl->shared_mem != MAP_FAILED);

	g_ctrl->shared_mem_len = len;

	return 0;
}

static void append_to_log_file(struct timespec *ts, char *buf1, int len1,
		char *buf2, int len2)
{
	struct tm tm;
	char tsbuf[CTIME_BUF_LEN];
	int len;
	int ret;

	localtime_r(&ts->tv_sec, &tm);
	len = strftime(tsbuf, sizeof(tsbuf), "\n%D %T", &tm);
	len += snprintf(tsbuf+len, sizeof(tsbuf)-len, ".%06ld ",
		ts->tv_nsec/1000);

	ret = write(g_ctrl->log_fd, tsbuf, len);
	if (ret != len) {
		fprintf(stderr, "fd=%d, ret=%d, len=%d\n",
			g_ctrl->log_fd, ret, len);
		perror("write");
	}

	assert(ret == len);

	if (len1 > 0) {
		ret = write(g_ctrl->log_fd, buf1, len1);
		assert(ret == len1);
	}
	if (len2 > 0) {
		ret = write(g_ctrl->log_fd, buf2, len2);
		assert(ret == len2);
	}
}

static void append_newline(void)
{
	char buf[10];
	int len, ret;

	len = snprintf(buf, sizeof(buf), "\n");
	ret = write(g_ctrl->log_fd, buf, len);
	assert(ret == len);
}

static void write_one_entry(struct hlog_ring *r)
{
	char *buf1, *buf2;
	int len1, len2;
	int next_tail;
	struct timespec log_ts;
	int prio;

	_ring_peak(r, &log_ts, &prio, &buf1, &len1, &buf2, &len2, &next_tail);
	if (prio >= g_ctrl->log_all_prio)
		append_to_log_file(&log_ts, buf1, len1, buf2, len2);
	_ring_deq(r, next_tail);
}

#define MAX_IDLE_LOOP 1000
static void *logger_thr_func(void *arg)
{
	struct timespec ts, *kp;
	struct hlog_thr *thr;
	struct hlog_ring *r;
	long nr_idle_loop;
	int q;

	HLOG_NOTICE("logger start");
	nr_idle_loop = 0;
	pthread_mutex_lock(&g_ctrl->mutex);
	clock_gettime(CLOCK_REALTIME, &g_ctrl->last_refresh);
	lt_refresh(g_ctrl->lt, g_ctrl->nr_thr);
	while (!g_ctrl->exit || nr_idle_loop==0) {
		/*
		 * logger线程有POLL和WAIT两个状态，在POLL状态并不等待条件变量,
		 * 而是以轮询方式工作，这样可以避免线程上下文切换，获得比较好
		 * 的性能。只有当空转的轮询次数超过一个上限的时候，才转入WAIT
		 * 状态。
		 */
		if (LOAD(g_ctrl->status) == LS_WAIT) {
			clock_gettime(CLOCK_REALTIME, &ts);
			ts_add_ns(&ts, 1000000L);

			pthread_cond_timedwait(&g_ctrl->logger_cond,
				&g_ctrl->mutex, &ts);
			nr_idle_loop = 0;
			g_ctrl->nr_wait++;
		}

		if (nr_idle_loop > MAX_IDLE_LOOP)
			STORE(g_ctrl->status, LS_WAIT);

		//pop the oldest entry and then write it to log file
		lt_top(g_ctrl->lt, &q);
		get_key(q, (void **)&kp);

		/*
		 * 从败者树取出来的key如果是max_ts，说明本轮已经所有日志已经取
		 * 出了，所以需要重新刷新时戳.
		 */
		if (is_max_ts(kp)) {
			clock_gettime(CLOCK_REALTIME, &g_ctrl->last_refresh);
			lt_refresh(g_ctrl->lt, g_ctrl->nr_thr);
			nr_idle_loop++;
			g_ctrl->nr_refresh++;
			continue;
		}

		g_ctrl->nr_logs++;
		thr = get_thr(q);
		pthread_mutex_unlock(&g_ctrl->mutex);
		pthread_mutex_lock(&thr->mutex);
		r = thr->ring;
		write_one_entry(r);
		lt_adjust(g_ctrl->lt, q);

		//如果业务线程因为ring满而阻塞，则唤醒它
		if (thr->blocked) {
			thr->blocked = 0;
			pthread_cond_signal(&thr->thr_cond);
			g_ctrl->nr_wakeup++;
		}
		pthread_mutex_unlock(&thr->mutex);
		pthread_mutex_lock(&g_ctrl->mutex);

	}
	append_newline();
	pthread_mutex_unlock(&g_ctrl->mutex);
	return NULL;
}

/* 初始化hlog模块 */
int hlog_init(char *log_file, char *mmap_file, int persist_prio,
		int log_all_prio, int last_words_piro)
{
	int ret, len;

	assert(g_ctrl == NULL);
	assert(sizeof(struct hlog_entry_hdr) <= HLOG_ENTRY_ALIGN);

	g_ctrl = (struct hlog_ctrl *)calloc(1, sizeof(*g_ctrl));
	if (g_ctrl == NULL) {
		return ENOMEM;
	}

	g_ctrl->page_size = (int)sysconf(_SC_PAGESIZE);
	assert(g_ctrl->page_size>0);

	//确保pagesize是2的正数次幂
	assert(((g_ctrl->page_size -1) & g_ctrl->page_size) == 0);

	g_ctrl->lt = lt_create(0, HLOG_MAX_THR, get_key, (cmpkey_t)cmp_ts);

	g_ctrl->persist_prio = persist_prio;
	g_ctrl->log_all_prio = log_all_prio;
	g_last_words_piro = last_words_piro;

	ret = pthread_mutex_init(&g_ctrl->mutex, NULL);
	assert(ret == 0);

	ret = pthread_cond_init(&g_ctrl->logger_cond, NULL);
	assert(ret == 0);

	/* 打开日志文件 */
	len = strlen(log_file);
	assert(len < HLOG_PATH_MAX-1);
	strncpy(g_ctrl->log_file, log_file, HLOG_PATH_MAX-1);

	g_ctrl->log_fd = open(log_file, O_RDWR|O_CREAT|O_APPEND, 0644);
	if (g_ctrl->log_fd < 0) {
		perror("open");
		return errno;
	}

	/* 初始化共享内存区 */
	len = strlen(mmap_file);
	assert(len < HLOG_PATH_MAX-1);
	strncpy(g_ctrl->mmap_file, mmap_file, HLOG_PATH_MAX-1);
	ret = init_shared_mem();
	if (ret != 0)
		return ret;

	recover_last_words();
	memset(g_ctrl->shared_mem, 0, g_ctrl->shared_mem_len);

	ret = pthread_create(&g_ctrl->logger_thr, NULL, logger_thr_func, NULL);
	assert(ret == 0);
	return 0;
}

void hlog_exit(void)
{
	pthread_mutex_lock(&g_ctrl->mutex);
	g_ctrl->exit = 1;
	pthread_mutex_unlock(&g_ctrl->mutex);
	pthread_cond_signal(&g_ctrl->logger_cond);
	pthread_join(g_ctrl->logger_thr, NULL);

	(void)munmap(g_ctrl->shared_mem, g_ctrl->shared_mem_len);
	(void)close(g_ctrl->mmap_fd);
	assert(g_ctrl != NULL);
	assert(g_ctrl->log_fd >= 0);
	(void)close(g_ctrl->log_fd);
	lt_destroy(g_ctrl->lt);
	free(g_ctrl);
	g_ctrl = NULL;
}


